package com.xj.opcua.server;

import java.util.List;
import java.util.Map;

import org.eclipse.milo.opcua.sdk.core.AccessLevel;
import org.eclipse.milo.opcua.sdk.core.Reference;
import org.eclipse.milo.opcua.sdk.server.OpcUaServer;
import org.eclipse.milo.opcua.sdk.server.api.DataItem;
import org.eclipse.milo.opcua.sdk.server.api.ManagedNamespaceWithLifecycle;
import org.eclipse.milo.opcua.sdk.server.api.MonitoredItem;
import org.eclipse.milo.opcua.sdk.server.nodes.UaFolderNode;
import org.eclipse.milo.opcua.sdk.server.nodes.UaVariableNode;
import org.eclipse.milo.opcua.sdk.server.util.SubscriptionModel;
import org.eclipse.milo.opcua.stack.core.AttributeId;
import org.eclipse.milo.opcua.stack.core.Identifiers;
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;

import com.xj.opcua.client.manager.DataTypeDictionaryManager;
import com.xj.opcua.filter.AttributeLoggingFilter;

public class OpcNameSpace extends ManagedNamespaceWithLifecycle {
	
    private final DataTypeDictionaryManager dictionaryManager;

    private final SubscriptionModel subscriptionModel;

    public OpcNameSpace(OpcUaServer server) {
        super(server, CreateAndAddNodes.NAMESPACE_URI3);

        subscriptionModel = new SubscriptionModel(server, this);
        dictionaryManager = new DataTypeDictionaryManager(getNodeContext(), CreateAndAddNodes.NAMESPACE_URI3);

        getLifecycleManager().addLifecycle(dictionaryManager);
        getLifecycleManager().addLifecycle(subscriptionModel);

        //	添加信号
        getLifecycleManager().addStartupTask(this::createAndAddTestNodes);

    }

    /**
     * 	添加opc节点信息
     */
    private void createAndAddTestNodes() {
        // Create a "mes" folder and add it to the node manager
        NodeId folderNodeId = newNodeId("mes");

        UaFolderNode folderNode = new UaFolderNode(
            getNodeContext(),
            folderNodeId,
            newQualifiedName("mes"),
            LocalizedText.english("mes")
        );

        getNodeManager().addNode(folderNode);

        // Make sure our new folder shows up under the server's Objects folder.
        folderNode.addReference(new Reference(
            folderNode.getNodeId(),
            Identifiers.Organizes,
            Identifiers.ObjectsFolder.expanded(),
            false
        ));

        // Add the rest of the nodes
        addVariableNodes(folderNode);
    }
    
    private void addVariableNodes(UaFolderNode rootNode) {
    	if(Server.opcNodesList!=null && Server.opcNodesList.size()>0) {
    		for(Map<String, List<Object[]>> map:Server.opcNodesList) {
    			if(map==null || map.size()==0) continue;
    			
				if(map.containsKey("folder") && map.containsKey("nodes")) {
					Object folder[] =  map.get("folder").get(0);
					UaFolderNode folderNode = addFolder(rootNode,folder);
					if(folderNode==null) continue;
					List<Object[]> listNodes = map.get("nodes");
					if(listNodes==null || listNodes.size()==0) continue;
					addNodes(folderNode,listNodes);
				}
    		}
    	}
    }
    
    private UaFolderNode addFolder(UaFolderNode rootNode,Object folder[]) {
    	if(folder.length!=3) return null;
        UaFolderNode arrayTypesFolder = new UaFolderNode(
            getNodeContext(),
            newNodeId(folder[0].toString()),
            newQualifiedName(folder[1].toString()),
            LocalizedText.english(folder[1].toString())
        );
        getNodeManager().addNode(arrayTypesFolder);
        rootNode.addOrganizes(arrayTypesFolder);
        return arrayTypesFolder;
    }

    private void addNodes(UaFolderNode rootNode,List<Object[]> listNodes) {
        for (Object[] os : listNodes) {
        	if(os.length!=3) continue;
        	Object[] osType = CreateAndAddNodes.getDataType(os[2].toString());
            UaVariableNode node = new UaVariableNode.UaVariableNodeBuilder(getNodeContext())
                .setNodeId(newNodeId(os[0].toString()))
                .setAccessLevel(AccessLevel.READ_WRITE)
                .setUserAccessLevel(AccessLevel.READ_WRITE)
                .setBrowseName(newQualifiedName(os[1].toString()))
                .setDisplayName(LocalizedText.english(os[1].toString()))
                .setDataType((NodeId)osType[1])
                .setTypeDefinition(Identifiers.BaseDataVariableType)
                .build();

            node.setValue(new DataValue((Variant)osType[2]));

            node.getFilterChain().addLast(new AttributeLoggingFilter(AttributeId.Value::equals));

            getNodeManager().addNode(node);
            rootNode.addOrganizes(node);
        }
    }

    @Override
    public void onDataItemsCreated(List<DataItem> dataItems) {
        subscriptionModel.onDataItemsCreated(dataItems);
    }

    @Override
    public void onDataItemsModified(List<DataItem> dataItems) {
        subscriptionModel.onDataItemsModified(dataItems);
    }

    @Override
    public void onDataItemsDeleted(List<DataItem> dataItems) {
        subscriptionModel.onDataItemsDeleted(dataItems);
    }

    @Override
    public void onMonitoringModeChanged(List<MonitoredItem> monitoredItems) {
        subscriptionModel.onMonitoringModeChanged(monitoredItems);
    }

}
